from testing.flow import TestFlow
from testing.scenario import Scenario
from google.appengine.ext import db
from app.helpers.queries import Query
from app.models.valuesets import ValueSet
from app.models.versions import Version
from app.models.tags import Tag
from app.models.fieldsets import FieldSet

tf = TestFlow('ValueSet Scenario Testing')

def start(flow):
  flow.scenario = Scenario()
  flow.interface = flow.scenario.get_interface()
  
@tf.checks(start, tf)
def f(flow, ret):
  """ Checks start was clean """
  dataset = flow.scenario.dataset
  return flow.check_equal(ValueSet.all().filter('dataset_key = ', dataset.key()).fetch(1), [])

def get_primary_model(flow):
  """ Converts fieldset to model """
  i = flow.interface
  f = flow.scenario.primary_fieldset
  return i.get_model(f.key())
  
@tf.checks(get_primary_model, tf)
def f(flow, ret):
  x = flow.check(ret)
  x = flow.check(ret._default_dataset_key) and x
  x = flow.check(ret._default_fieldset_key) and x
  x = flow.check(ret._field_order) and x
  return x

def insert_or_update_row(flow):
  """ Creates ValueSets for new user """
  i = flow.interface
  s = flow.scenario
  return i.insert_or_update_row(
    (s.primary_fieldset.key(),
      {'name' : 'Squir El',
      'email' : 'squirrel.appengine@gmail.com'}),
    (s.tags_fieldset.key(),
      {'skills' : 'Web Development, typing',
      'interests' : ['Acorns', 'Digging']}))

@tf.checks(insert_or_update_row, tf)
def f(flow, ret):
  # Creates two rows and they're both stored in interface
  flow.check_equal(len(ret), 2)
  flow.check_equal(len(flow.interface._valuesets), 2)
  for valueset in ret:
    flow.check_in(valueset.key(), flow.interface._valuesets)
  
  # Only one primary
  flow.check(ret[0].is_primary)
  flow.check(not ret[1].is_primary)
  
  # Check primary ValueSet dataset and fieldset
  flow.check_equal(ret[0].dataset_key, flow.scenario.dataset.key())
  flow.check_equal(ret[0].fieldset_key, flow.scenario.primary_fieldset.key())
  
  # Check primary values are set to Python objects that can
  # handle variance in string representations
  flow.check_equal(ret[0].email, 'Squirrel.appengine@gmail.com')
  flow.check_equal(ret[0].name, 'El, Squir')
  
  # Check secondary ValueSet dataset and fieldset
  flow.check_equal(ret[1].dataset_key, flow.scenario.dataset.key())
  flow.check_equal(ret[1].fieldset_key, flow.scenario.tags_fieldset.key())
  
  # Check secondary values are set to Python objects
  flow.check_in('web_development', ret[1].skills)
  flow.check_in('Typing', ret[1].skills)
  flow.check_in('acorns', ret[1].interests)
  flow.check_in('digging', ret[1].interests)
    
  # Check agg sort and lookups are synced
  flow.check(ret[0].agg_sort)
  flow.check(ret[1].lookups)
  flow.check_equal(ret[0].agg_sort, ret[1].agg_sort)
  flow.check_equal(ret[1].lookups, ret[1].lookups)
  # Primary unique agg sort should come first
  flow.check(ret[0].unique_agg_sort < ret[1].unique_agg_sort)
  
  # Check tags have been created
  tfk = flow.scenario.tags_fieldset.key()
  tag1 = Tag.get_key_name(tfk, 'skills', 'webdevelopment')
  tag2 = Tag.get_key_name(tfk, 'skills', 'typing')
  tag3 = Tag.get_key_name(tfk, 'interests', 'acorns')
  tag4 = Tag.get_key_name(tfk, 'interests', 'digging')
  flow.check_in(tag1, flow.interface._tags)
  flow.check_in(tag2, flow.interface._tags)
  flow.check_in(tag3, flow.interface._tags)
  flow.check_in(tag4, flow.interface._tags)
  flow.check_equal(4, len(flow.interface._tags))
  
  # Check tag values
  flow.check_equal(flow.interface._tags[tag1].dataset_key, flow.scenario.dataset.key())
  flow.check_equal(flow.interface._tags[tag1].fieldset_key, tfk)
  flow.check_equal(flow.interface._tags[tag1].property_name, 'skills')
  flow.check_equal(flow.interface._tags[tag1].verbose_property, 'Skills')
  flow.check_equal(flow.interface._tags[tag1].simplified_value, 'webdevelopment')
  flow.check_equal(flow.interface._tags[tag1].verbose_value, 'Web Development')
  flow.check_equal(flow.interface._tags[tag1].count, 1)
  
  flow.check_equal(flow.interface._tags[tag2].property_name, 'skills')
  flow.check_equal(flow.interface._tags[tag2].verbose_value, 'typing')
  
  flow.check_equal(flow.interface._tags[tag3].property_name, 'interests')
  flow.check_equal(flow.interface._tags[tag3].verbose_value, 'Acorns')
  
  flow.check_equal(flow.interface._tags[tag4].property_name, 'interests')
  flow.check_equal(flow.interface._tags[tag4].verbose_value, 'Digging')
  
  flow.tags = flow.interface._tags
  
  # FieldSet counts
  flow.check_equal(flow.interface._fieldset_counts, {
    flow.scenario.primary_fieldset.key() : 1,
    flow.scenario.tags_fieldset.key() : 1})
  
  flow.primary = ret[0]
  flow.tags_valueset = ret[1]

def commit(flow):
  """ Commits ValueSets we just created to datastore """
  valuesets = flow.interface._valuesets.values()
  flow.interface.commit()
  return valuesets

@tf.checks(commit, tf)
def f(flow, ret):
  # Check that only two valuesets saved
  dataset = flow.scenario.dataset
  valuesets = ValueSet.all().filter('dataset_key = ', dataset.key()).fetch(10)
  flow.check(len(valuesets), 2)
  
  # Check that keys match
  keys_pre = [valueset.key() for valueset in ret]
  keys_post = [valueset.key() for valueset in valuesets]
  flow.check_equal(set(keys_pre), set(keys_post))
  
  # Check versions
  versions = Version.all().filter('dataset_key = ', dataset.key()).fetch(10)
  flow.check_equal(len(versions), 2)
  
  # Check that versions correspond to valuesets
  for v in versions:
    flow.check_in(v.current.key(), keys_post)
    
  # Valuesets emptied
  flow.check(not flow.interface._valuesets)
  
  # No fieldset_key change
  flow.check_equal(ValueSet.get(flow.primary.key()).fieldset_key,
    flow.primary.fieldset_key)
  
  # Check tags saved
  for tag_name, tag in flow.tags.items():
    db_tag = Tag.get_by_key_name(tag_name)
    flow.check(db_tag)
    if db_tag:
      flow.check_equal(db_tag.count, 1)
      flow.check_equal(db_tag.verbose_value, tag.verbose_value)
  
  # Check fieldsets updated
  flow.check_equal(FieldSet.get(flow.scenario.primary_fieldset.key()).count, 1)
  flow.check_equal(FieldSet.get(flow.scenario.tags_fieldset.key()).count, 1)
  
  # Tags emptied
  flow.check(not flow.interface._tags)
  
  # FieldSet counts reset
  flow.check(not flow.interface._fieldset_counts)

def update_row(flow):
  """ Upates existing valuesets, adds new secondary """
  i = flow.interface
  s = flow.scenario
  return i.insert_or_update_row(
    (s.primary_fieldset.key(),
      {'name' : 'El, Squir',
      'phone_number' : '555-555-5555'}),
    (s.tags_fieldset.key(),
      {'skills' : 'Typing'}),
    (s.cat_fieldset.key(),
      {'neighborhood' : 'Everywhere',
      'favorite_color' : 'Green'}))

@tf.checks(update_row,tf)
def f(flow, ret):
  # Check primary is still the same as previous
  flow.check_equal(ret[0].key(), flow.primary.key())
  flow.check_equal(ret[0].name, flow.primary.name)
  flow.check_equal(ret[0].email, flow.primary.email)
  flow.check(ret[0].email)
    
  # Check to see if new value was added and converted to Python object
  flow.check_equal(ret[0].phone_number, '5555555555')
  
  # Check if aggsort has changed
  flow.check_not_equal(ret[0].agg_sort, flow.primary.agg_sort)
  flow.check_not_equal(ret[0].unique_agg_sort, flow.primary.unique_agg_sort)
  
  # Check changes to tags fieldset
  flow.check_in('typing', ret[1].skills)
  flow.check_not_in('web development', ret[1].skills)
  
  # Check no changes to existing data in fieldset
  flow.check_in('digging', ret[1].interests)
  flow.check_in('acorns', ret[1].interests)
  
  # Check new secondary valueset
  flow.check_equal(ret[2].dataset_key, flow.scenario.dataset.key())
  flow.check_equal(ret[2].fieldset_key, flow.scenario.cat_fieldset.key())
  
  # Check secondary values are set to Python objects
  flow.check_equal('everywhere!!!', ret[2].neighborhood)
  flow.check_equal('g r e e n', ret[2].favorite_color)
  
  # Check tags have been created
  tfk = flow.scenario.tags_fieldset.key()
  tag1 = Tag.get_key_name(tfk, 'skills', 'webdevelopment')
  tfk2 = flow.scenario.cat_fieldset.key()
  tag2 = Tag.get_key_name(tfk2, 'neighborhood', 'everywhere')
  tag3 = Tag.get_key_name(tfk2, 'favorite_color', 'green')
  flow.check_in(tag1, flow.interface._tags)
  flow.check_in(tag2, flow.interface._tags)
  flow.check_in(tag3, flow.interface._tags)
  flow.check_equal(3, len(flow.interface._tags))
  
  # Check tag counts
  flow.check_equal(flow.interface._tags[tag1].count, -1)
  flow.check_equal(flow.interface._tags[tag2].count, 1)
  flow.check_equal(flow.interface._tags[tag3].count, 1)
  
  # Check fieldset count incrases only for new fieldset
  flow.check_equal(flow.interface._fieldset_counts, {
    flow.scenario.cat_fieldset.key() : 1})
  
  flow.tags = flow.interface._tags

def commit2(flow):
  """ Commits ValueSets we just created to datastore """
  valuesets = flow.interface._valuesets.values()
  flow.interface.commit()
  return valuesets

@tf.checks(commit2, tf)
def f(flow, ret):
  # Check that only three valuesets saved (2 existing + 1 new)
  dataset = flow.scenario.dataset
  valuesets = ValueSet.all().filter('dataset_key = ', dataset.key()).fetch(10)
  flow.check(len(valuesets), 3)
  
  # Check that keys match
  keys_to_save = [valueset.key() for valueset in ret]
  keys_actually_saved = [valueset.key() for valueset in valuesets]
  for key in keys_to_save:
    flow.check_in(key, keys_actually_saved)
  
  # Check versions
  versions = Version.all().filter('dataset_key = ', dataset.key()).fetch(10)
  flow.check_equal(len(versions), 5)
  
  # Check primary has two saved versions
  saved_keys = {}
  for v in versions:
    saved_keys.setdefault(v.current.key(), 0)
    saved_keys[v.current.key()] += 1
  flow.check_equal(saved_keys[flow.primary.key()], 2)
  flow.check_equal(saved_keys[flow.tags_valueset.key()], 2)
  
  # Check that we can lookup by new primary value
  lookup_results = [r.key()
    for r in flow.interface.lookup_primary('555-555-5555')]
  flow.check_equal(len(lookup_results), 1)
  flow.check_equal(lookup_results[0], flow.primary.key())
  
  # Check secondaries updated with new lookups, agg_sorts, related_fieldsets
  primary = ValueSet.get(flow.primary.key())
  for valueset in ValueSet.all().filter('primary_key = ', flow.primary.key()):
    fieldset = flow.interface.get_fieldset(valueset.fieldset_key)
    flow.msg('Checking for flow.primary, fieldset %s' % fieldset.verbose_name)
    flow.check_equal(primary.lookups, valueset.lookups)
    flow.check_equal(primary.agg_sort, valueset.agg_sort)
    if valueset.key() != primary.key():
      flow.check_not_equal(primary.unique_agg_sort, valueset.unique_agg_sort)
    flow.check_equal(primary.related_fieldsets, valueset.related_fieldsets)
  
  # Valuesets emptied
  flow.check(not flow.interface._valuesets)
  
  # Check tags updated
  tag1 = Tag.get_key_name(flow.scenario.tags_fieldset.key(),
    'skills', 'webdevelopment')
  flow.check_equal(Tag.get_by_key_name(tag1).count, 0)
  
  # Tags emptied
  flow.check(not flow.interface._tags)

def add_row(flow):
  """ Add new row because primary user data differs a little """
  i = flow.interface
  s = flow.scenario
  return i.insert_or_update_row(
    (s.primary_fieldset.key(),
      {'name' : 'El, Squir',
      'email' : 'different.email@gmail.com'}),
    (s.cat_fieldset.key(),
      {'neighborhood' : 'England',
      'favorite_color' : 'Red'}))

@tf.checks(add_row,tf)
def f(flow, ret):
  # Check new primary
  flow.check_not_equal(ret[0].key(), flow.primary.key())
  
  # Check primary relationship
  flow.check(ret[0].is_primary)
  flow.check(not ret[1].is_primary)
  flow.check_equal(ret[1].primary_key, ret[0].key())
  
  # Check primary ValueSet dataset and fieldset
  flow.check_equal(ret[0].dataset_key, flow.scenario.dataset.key())
  flow.check_equal(ret[0].fieldset_key, flow.scenario.primary_fieldset.key())
  
  # Check primary values are set to Python objects that can
  # handle variance in string representations
  flow.check_equal(ret[0].email, 'different.email@gmail.com')
  flow.check(not ret[0].phone_number)
  
  # Check secondary ValueSet dataset and fieldset
  flow.check_equal(ret[1].dataset_key, flow.scenario.dataset.key())
  flow.check_equal(ret[1].fieldset_key, flow.scenario.cat_fieldset.key())
  
  # Check secondary values are set to Python objects
  flow.check_equal('england', ret[1].neighborhood)
  flow.check_equal('red', ret[1].favorite_color)
  
  # Check agg sort
  flow.check(ret[1].agg_sort < flow.primary.agg_sort)
  flow.check(ret[1].unique_agg_sort < flow.primary.unique_agg_sort)

def modify_primary(flow):
  i = flow.interface
  s = flow.scenario
  p = flow.primary
  return i.update_by_primary_key(p.key(),
    (s.primary_fieldset.key(),
      {'name' : 'El, Squir',
      'email' : 'different.email.2@gmail.com'}),
    (s.cat_fieldset.key(),
      {'neighborhood' : 'Lancaster'}))

@tf.checks(modify_primary, tf)
def f(flow, ret):
  # Check primary key has not changed
  flow.check_equal(ret[0].key(), flow.primary.key())
  
  # Check that some values have changed
  primary = flow.interface._valuesets[flow.primary.key()]
  flow.check_equal(primary.name, 'Squir El')
  flow.check_equal(primary.email, 'different.email.2@gmail.com')
  
  # Check that others have not
  flow.check_equal(primary.phone_number, '5555555555')
  
  # Check for existence of new secondaries
  flow.check_equal(ret[1].neighborhood, 'Lancaster')
  
  # Check data from old secondary not lost
  flow.check(ret[1].favorite_color)

def multiple_tags(flow):
  """ Increment tag count by the right amount """
  i = flow.interface
  i.commit()
  s = flow.scenario
  i.insert_or_update_row(
    (s.primary_fieldset.key(),
      {'email' : 'someone@gmail.com'}),
    (s.tags_fieldset.key(),
      {'skills': 'typing'}),
    (s.cat_fieldset.key(),
      {'neighborhood' : 'Dorchester'}))
  i.insert_or_update_row(
    (s.primary_fieldset.key(),
      {'email' : 'someone.else@gmail.com'}),
    (s.tags_fieldset.key(),
      {'skills': 'typing'}),
    (s.cat_fieldset.key(),
      {'neighborhood' : 'Dorchester'}))
  i.commit()

@tf.checks(multiple_tags, tf)
def f(flow, ret):
  tag_name = Tag.get_key_name(flow.scenario.tags_fieldset.key(),
    'skills', 'typing')
  flow.check_equal(Tag.get_by_key_name(tag_name).count, 3)
  
  tag_name = Tag.get_key_name(flow.scenario.cat_fieldset.key(),
    'neighborhood', 'Dorchester')
  flow.check_equal(Tag.get_by_key_name(tag_name).count, 2)
  
  f = FieldSet.get(flow.scenario.primary_fieldset.key())
  flow.check_equal(f.count, len(Query(ValueSet).filter(fieldset_key=f.key())))
  
  f = FieldSet.get(flow.scenario.cat_fieldset.key())
  flow.check_equal(f.count, len(Query(ValueSet).filter(fieldset_key=f.key())))
  
  f = FieldSet.get(flow.scenario.tags_fieldset.key())
  flow.check_equal(f.count, len(Query(ValueSet).filter(fieldset_key=f.key())))

def remove_multiple(flow):
  """ Decrement tag count by right amount """
  i = flow.interface
  i.commit()
  s = flow.scenario
  i.insert_or_update_row(
    (s.primary_fieldset.key(),
      {'email' : 'someone@gmail.com'}),
    (s.tags_fieldset.key(),
      {'skills': 'not typing'}))
  i.insert_or_update_row(
    (s.primary_fieldset.key(),
      {'email' : 'someone.else@gmail.com'}),
    (s.tags_fieldset.key(),
      {'skills': 'not typing'}))
  i.commit()

@tf.checks(remove_multiple, tf)
def f(flow, ret):
  tag_name = Tag.get_key_name(flow.scenario.tags_fieldset.key(),
    'skills', 'typing')
  flow.check_equal(Tag.get_by_key_name(tag_name).count, 1)


